﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using ComponentAce.Compression.Libs.zlib;

namespace com.lover.common.http
{
    public class HttpResult
    {
        private Uri _currentUri;
        /// <summary>
        /// URL
        /// </summary>
        public Uri CurrentUri
        {
            get { return _currentUri; }
            set { _currentUri = value; }
        }
        private int _statusCode = 0;
        /// <summary>
        /// status code
        /// </summary>
        public int StatusCode { get { return _statusCode; } }
        private string _contentType = "";
        /// <summary>
        /// content type
        /// </summary>
        public string ContentType { get { return _contentType; } }
        private int _contentLength = -1;
        /// <summary>
        /// content length
        /// </summary>
        public int ContentLength { get { return _contentLength; } }
        private bool _gzip = false;
        /// <summary>
        /// 是否使用gzip压缩
        /// </summary>
        public bool Gzip
        {
            get { return _gzip; }
            set { _gzip = value; }
        }
        private bool _chunked = false;
        /// <summary>
        /// 是否分块传输数据
        /// </summary>
        public bool Chunked
        {
            get { return _chunked; }
            set { _chunked = value; }
        }
        private Encoding _encoding = Encoding.UTF8;
        /// <summary>
        /// 使用的编码
        /// </summary>
        public Encoding Encoding
        {
            get { return _encoding; }
            set { _encoding = value; }
        }
        private MemoryStream _contentStream;
        /// <summary>
        /// 内容流
        /// </summary>
        public MemoryStream ContentStream
        {
            get { return _contentStream; }
            set { _contentStream = value; }
        }
        private List<Cookie> _cookies = new List<Cookie>();
        public List<Cookie> getCookies() { return _cookies; }
        private Dictionary<string, string> _extraHeaders = new Dictionary<string, string>();
        public string getExtraHeader(string key)
        {
            if (key == null || key.Length == 0) return null;
            string keylower = key.ToLower();
            if (_extraHeaders.ContainsKey(keylower)) return _extraHeaders[keylower]; 
            return null;
        }
        /// <summary>
        /// 按照指定的编码获取内容字符串
        /// </summary>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public string getContent(Encoding encoding)
        {
            _encoding = encoding;
            return getContent();
        }
        /// <summary>
        /// 获取内容字符串
        /// </summary>
        /// <returns></returns>
        public string getContent()
        {
            if (_contentStream == null) return "";
            if (_gzip)
            {
                MemoryStream outst = new MemoryStream();
                GZipStream gzipstream = new GZipStream(_contentStream, CompressionMode.Decompress);
                gzipstream.Flush();

                MemoryStream outBuffer = new MemoryStream();
                byte[] block = new byte[10240];
                while (true)
                {
                    int bytesRead = gzipstream.Read(block, 0, block.Length);
                    if (bytesRead <= 0) break;
                    else outBuffer.Write(block, 0, bytesRead);
                }
                outBuffer.Seek(0, SeekOrigin.Begin);

                StreamReader sr = new StreamReader(outBuffer, _encoding);
                string content = sr.ReadToEnd();
                sr.Close();
                return content;
            }
            else
            {
                StreamReader sr = new StreamReader(_contentStream, _encoding);
                string content = sr.ReadToEnd();
                sr.Close();
                return content;
            }
        }
        public void parseHeader(string header_string)
        {
            if (header_string == null || header_string.Length == 0) return;
            HttpResult result = new HttpResult();

            string[] headers = header_string.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
            foreach (string header in headers)
            {
                if (header == null || header.Length == 0) continue;
                if (header.ToLower().StartsWith("http/")) parseStatus(header);
                else if (header.ToLower().StartsWith("content-type")) _contentType = removePrefix(header);
                else if (header.ToLower().StartsWith("content-encoding")) _gzip = removePrefix(header).Contains("gzip");
                else if (header.ToLower().StartsWith("transfer-encoding")) _chunked = removePrefix(header).Contains("chunked");
                else if (header.ToLower().StartsWith("content-length")) Int32.TryParse(removePrefix(header), out _contentLength);
                else if (header.ToLower().StartsWith("set-cookie")) parseCookie(removePrefix(header));
                else
                {
                    int comma_index = header.IndexOf(':');
                    if (comma_index < 0) continue;
                    string key = header.Substring(0, comma_index);
                    string value = header.Substring(comma_index + 1).Trim();
                    addExtraHeader(key.ToLower(), value);
                }
            }
        }
        private void parseStatus(string status)
        {
            // HTTP/1.1 302 Found | HTTP/1.1 200 OK
            if (status == null || status.Length == 0) return;
            string[] status_strs = status.Split(' ');
            if (status_strs.Length < 3) return;
            Int32.TryParse(status_strs[1], out _statusCode);
        }
        private void parseCookie(string cookie_string)
        {
            if (cookie_string == null || cookie_string.Length == 0) return;
            string[] cook_parts = cookie_string.Split(';');
            Cookie cook = new Cookie();
            int part_index = 0;
            foreach (string cook_part in cook_parts)
            {
                if (cook_part == null || cook_part.Length == 0) continue;
                int equal_index = cook_part.IndexOf('=');
                if (equal_index < 0) continue;
                string key = cook_part.Substring(0, equal_index).Trim();
                string value = cook_part.Substring(equal_index + 1).Trim();

                part_index++;
                if(part_index == 1)
                {
                    cook.Name = key; cook.Value = value;
                    continue;
                }

                if (key.ToLower().Equals("path")) cook.Path = value;
                else if (key.ToLower().Equals("domain")) cook.Domain = value;
                else if (key.ToLower().Equals("expires"))
                {
                    DateTime dt;
                    bool parse_ok = DateTime.TryParse(value, out dt);
                    if (parse_ok) cook.Expires = dt;
                }
            }
            if (cook.Path == null || cook.Path.Length == 0) cook.Path = _currentUri.AbsolutePath;
            if (cook.Domain == null || cook.Domain.Length == 0) cook.Domain = _currentUri.Host;

            _cookies.Add(cook);
        }
        private void addExtraHeader(string key, string value)
        {
            if (_extraHeaders.ContainsKey(key))
            {
                _extraHeaders[key] = _extraHeaders[key] + ", " + value;
            }
            else _extraHeaders.Add(key, value);
        }
        private string removePrefix(string input_str)
        {
            if (input_str == null || input_str.Length == 0) return "";
            string result = input_str.Substring(input_str.IndexOf(':') + 1);
            result = result.Trim();
            return result;
        }
        public static void copyStream(System.IO.Stream input, System.IO.Stream output)
        {
            byte[] buffer = new byte[2000];
            int len;
            while ((len = input.Read(buffer, 0, 2000)) > 0)
            {
                output.Write(buffer, 0, len);
            }
            output.Flush();
        }
        public static int readAllBytesFromStream(Stream stream, byte[] buffer)
        {
            // Use this method is used to read all bytes from a stream.
            int offset = 0;
            int totalCount = 0;
            while (true)
            {
                int bytesRead = stream.Read(buffer, offset, 100);
                if (bytesRead == 0)
                {
                    break;
                }
                offset += bytesRead;
                totalCount += bytesRead;
            }
            return totalCount;
        }
    }
}
